@classmethod, @staticmethod, and @property

A decorator is a special kind of function that either takes a function
and returns a function, or takes a class and returns a class.
The @ symbol is just syntactic sugar that allows you to decorate
something in a way that’s easy to read.
The decorators
@classmethod,
@staticmethod and
@property
are used on functions defined within classes.
class MyClass(object):

    def __init__(self):
        self._some_property = "properties are nice"
        self._some_other_property = "VERY nice"

    def normal_method(*args,**kwargs):
        print("calling normal_method({0},{1})".format(args,kwargs))

    @classmethod
    def class_method(*args,**kwargs):
        print("calling class_method({0},{1})".format(args,kwargs))

    @staticmethod
    def static_method(*args,**kwargs):
        print("calling static_method({0},{1})".format(args,kwargs))

    @property
    def some_property(self,*args,**kwargs):
        print("calling some_property getter({0},{1},{2})".format(self,args,kwargs))
        return self._some_property

    @some_property.setter
    def some_property(self,*args,**kwargs):
        print("calling some_property setter({0},{1},{2})".format(self,args,kwargs))
        self._some_property = args[0]

    @property
    def some_other_property(self,*args,**kwargs):
        print("calling some_other_property getter({0},{1},{2})".format(self,args,kwargs))
        return self._some_other_property

o = MyClass()
# undecorated methods work like normal, they get the current instance (self) as the first argument

Normal methods

o.normal_method
# <bound method MyClass.normal_method of <__main__.MyClass instance at 0x7fdd2537ea28>>

o.normal_method()
# calling normal_method((<__main__.MyClass object at 0x00000000039C8208>,),{})

o.normal_method(1,2,x=3,y=4)
# calling normal_method((<__main__.MyClass object at 0x00000000039C8208>, 1, 2),{'x': 3, 'y': 4})

Class methods

Always get the class as the first argument
o.class_method
# <bound method classobj.class_method of <class __main__.MyClass at 0x7fdd2536a390>>

o.class_method()
# calling class_method((<class '__main__.MyClass'>,),{})

o.class_method(1,2,x=3,y=4)
# calling class_method((<class '__main__.MyClass'>, 1, 2),{'x': 3, 'y': 4})

Static methods

Have no arguments except the ones you pass in when you call them
o.static_method
# <function static_method at 0x7fdd25375848>

o.static_method()
# calling static_method((),{})

o.static_method(1,2,x=3,y=4)
# calling static_method((1, 2),{'x': 3, 'y': 4})

Properties

Are a way of implementing getters and setters.
It’s an error to explicitly call them “read only” attributes
can be specified by creating a getter without a setter
(as in some_other_property)
o.some_property
# calling some_property getter(<__main__.MyClass object at 0x00000000039E8EB8>,(),{})
# 'properties are nice'

o.some_property()
# calling some_property getter(<__main__.MyClass object at 0x00000000039E8EB8>,(),{})
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: 'str' object is not callable

o.some_other_property
# calling some_other_property getter(<__main__.MyClass instance at 0x7fb2b70877e8>,(),{})
# 'VERY nice'

o.some_other_property()
# calling some_other_property getter(<__main__.MyClass instance at 0x7fb2b70877e8>,(),{})
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: 'str' object is not callable

o.some_property = "groovy"
# calling some_property setter(<__main__.MyClass object at 0x7fb2b7077890>,('groovy',),{})

o.some_property
# calling some_property getter(<__main__.MyClass object at 0x7fb2b7077890>,(),{})
# 'groovy'

o.some_other_property = "very groovy"
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AttributeError: can't set attribute

o.some_other_property
# calling some_other_property getter(<__main__.MyClass object at 0x7fb2b7077890>,(),{})
# 'VERY nice'